15장. 관측 가능성
마이크로서비스에서는 하나의 요청이 한 서비스 안에서 끝나지 않는다.
예를 들어 사용자가 결제 버튼을 누르면 다음과 같은 흐름이 발생한다.
Client → API Gateway → Order Service → Payment Service → PG사
이 요청이 실패했을 때 우리는 이런 질문을 던지게 된다.
- Gateway 문제인가?
- Order 로직 문제인가?
- Payment 처리 문제인가?
- 외부 PG사 문제인가?
모놀리스였다면 하나의 로그 파일만 보면 되었겠지만,
마이크로서비스에서는 서비스마다 로그가 흩어져 있다.
그래서 관측 가능성이 필요하다.
관측 가능성은 단순히 “로그를 남기는 것”이 아니라,
요청의 흐름을 추적하고,
병목을 찾고,
정확한 원인을 식별할 수 있도록 설계하는 것
이다.
관측 가능성의 3가지 도구
관측 가능성은 세 가지 도구로 구성된다.
- 메트릭 (Metric)
- 트레이스 (Trace)
- 로그 (Log)
이 세 가지는 각각 다른 질문에 답한다.
| 도구 | 질문 | 역할 |
|---|---|---|
| 메트릭 | 문제가 있는가? | 이상 감지 |
| 트레이스 | 어디가 문제인가? | 병목 분석 |
| 로그 | 정확히 무슨 일이 있었는가? | 원인 분석 |
이 순서는 실제 장애 대응 순서이기도 하다.
1️⃣ 메트릭 — 시스템이 건강한지 보는 도구
메트릭은 시스템 상태를 숫자로 표현한다.
대표적인 메트릭:
- 요청 수 (RPS)
- 평균 응답 시간
- p95 / p99 지연 시간
- 에러율
- CPU / 메모리
- Consumer Lag
- DLQ 증가율
메트릭은 빠르게 이상을 감지한다.
예:
- 평소 결제 에러율 0.5%
- 오늘 4%로 상승
→ “문제가 있다”는 신호
하지만 메트릭은 원인을 알려주지 않는다.
메트릭은 경보 장치다.
원인 분석 도구는 아니다.
2️⃣ 트레이스 — 요청이 어디를 거쳤는지 보여준다
트레이스는 하나의 요청이 여러 서비스를 거치는 흐름을 보여준다.
트레이스는 두 개념으로 구성된다.
- Trace ID: 요청 전체를 대표하는 ID
- Span ID: 각 구간(서비스 호출)마다 부여되는 ID
하나의 요청은 하나의 Trace ID를 가진다.
각 서비스 구간은 별도의 Span으로 기록된다.
예:
| Trace ID | Span | 서비스 | 소요 시간 | 부모 |
|---|---|---|---|---|
| abc123 | span-1 | Gateway | 20ms | 없음 |
| abc123 | span-2 | Order | 40ms | span-1 |
| abc123 | span-3 | Payment | 2,900ms | span-2 |
| abc123 | span-4 | PG Call | 2,850ms | span-3 |
이를 시각화하면:
Trace ID: abc123
├─ Gateway 20ms
├─ Order 40ms
└─ Payment 2,900ms
└─ PG Call 2,850ms ← 병목
이걸 보면 바로 알 수 있다.
- 병목은 Payment
- 그 안에서도 PG Call이 대부분 차지
트레이스는 다음을 가능하게 한다.
- 구간별 소요 시간 분석
- 부모-자식 호출 관계 파악
- 병목 지점 식별
즉,
트레이스는 “구조와 시간”을 보여준다.
3️⃣ 로그와 Correlation ID — 요청을 묶는 방법
문제: 로그가 흩어져 있다
각 서비스는 각자 로그를 남긴다.
요청이 동시에 수천 개 들어오면 로그는 이렇게 섞인다.
[Order] 주문 생성 성공
[Payment] PG 호출 시작
[Order] 주문 생성 성공
[Payment] Timeout 발생
어떤 주문의 결제인지 알 수 없다.
해결: Correlation ID
Correlation ID는
하나의 요청에 붙는 사건 번호
다.
작동 방식은 단순하다.
- 외부 요청이 들어오면 ID를 하나 생성한다.
- 이 ID를 다음 서비스로 전달한다.
- 모든 서비스는 로그에 이 ID를 함께 기록한다.
- 문제가 생기면 이 ID로 전체 검색한다.
예:
[Gateway] CID=3f9c2a81 요청 시작
[Order] CID=3f9c2a81 주문 생성 성공
[Payment] CID=3f9c2a81 PG 호출
[Payment] CID=3f9c2a81 Timeout 발생
CID=3f9c2a81로 검색하면 이 요청의 전체 로그를 모을 수 있다.
중요한 점
Correlation ID는 묶기 전용이다.
- 이 로그들이 같은 요청에 속한다는 사실만 알려준다.
- 구간별 소요 시간이나 호출 관계는 보여주지 않는다.
Correlation ID vs Trace ID
Trace ID는 요청 전체를 대표한다.
Span ID는 각 구간을 나타낸다.
요즘은 Trace ID를 로그에도 같이 찍어서
Correlation ID 역할을 겸하는 경우가 많다.
즉,
Trace ID 하나로 로그 검색 + 트레이스 분석을 모두 처리하는 것이 일반적인 구조다.
비동기 이벤트에서의 ID 전파
비동기 구간에서는 Header가 없으므로
이벤트 본문에 traceId를 넣어야 한다.
{
"event": "OrderCreated",
"orderId": "123",
"traceId": "abc123"
}
이걸 하지 않으면 비동기 구간에서 추적이 끊긴다.
누가 ID를 만드는가?
원칙은 단순하다.
요청의 가장 바깥 경계가 생성한다.
보통은 API Gateway가 생성한다.
- 외부 요청 수신
- Trace ID 생성
- HTTP Header에 포함
- 내부 호출 시 자동 전달
중요한 점:
대부분은 개발자가 직접 구현하지 않는다.
OpenTelemetry 같은 라이브러리가:
- ID 생성
- Header 전파
- Span 생성
- 로그 필드 자동 삽입
을 처리한다.
개발자는:
- 로그 포맷에 traceId 포함
- 이벤트에 traceId 전달
정도만 신경 쓰면 된다.
실제 장애 대응 흐름
현실에서는 다음과 같이 진행된다.
1️⃣ 메트릭 알람 발생
- 결제 에러율 급증
2️⃣ 트레이스 확인
- 특정 구간에서 지연 발생
- Payment → PG 구간에서 병목
3️⃣ 로그 검색 (중앙 로그 시스템)
로그는 보통 중앙으로 수집된다.
예:
- ELK (Elasticsearch + Logstash + Kibana)
- OpenSearch
- CloudWatch Logs
운영자는 Kibana에서:
traceId: abc123
검색한다.
모든 서비스 로그가 한 번에 나온다.
서비스마다 SSH 접속해서 검색하지 않는다.
관측 가능성은 설계의 일부다
관측 가능성은 나중에 붙이는 기능이 아니다.
설계 단계에서 결정해야 한다.
- ID는 어디서 생성하는가?
- 동기/비동기 모두 전파되는가?
- 로그는 구조화되어 있는가?
- SLO 기준은 정의되어 있는가?
- Lag은 모니터링되는가?
관측 가능성이 없으면:
- 장애는 늦게 발견된다
- 원인 분석은 추측이 된다
- 복구 시간은 길어진다
관측 가능성이 있으면:
- 문제를 빠르게 감지하고
- 병목을 정확히 찾고
- 로그로 원인을 확정할 수 있다
이 장의 핵심
- 메트릭은 이상 감지 도구다.
- 트레이스는 병목 분석 도구다.
- 로그는 원인 분석 도구다.
- Trace ID는 분산 요청을 연결하는 핵심이다.
- ID 생성과 전파는 대부분 라이브러리가 처리한다.
- 로그는 ELK 같은 중앙 시스템에서 검색한다.
- 관측 가능성은 복원력의 기반이다.